Gestion de version avec git

J.-M. Bruel

Avant-propos

Pour suivre en live…​

La référence absolue (selon moi) !

Git et le BUT…​

but modules git

Environnement

git term

Principes généraux

Git c’est quoi ?

  • Un programme de gestion de version de code source (VCS — Version Control System)

  • Gratuit et open source

  • Distribué (contrairement à Subversion par exemple)

  • Créé à Linus Torvalds (2005)

linus git quote

Concepts clefs

  • Répertoire (directory or folder)

  • CLI: Command Line Interface

  • Dépôt (repository)

  • GitHub.com (ou GitLab.com ou gitlab.iut-blagnac.fr)

Avant de commencer

On initialise certaine variables (une fois pour toute en général) :

$ git config --global user.name "JM Bruel"
$ git config --global user.email jbruel@gmail.com
$ git config --global alias.co checkout

 

Ces informations sont stockées dans le fichier ~/.gitcongif.

Voici un extrait du mien :

[user]
        name = Jean-Michel Bruel
        email = jbruel@gmail.com
[alias]
        co = checkout
        st = status
        repo = config --get remote.origin.url

Ce qui donne :

$ git co
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
$ git checkout
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
$ git repo
https://github.com/jmbruel/teachingMaterials

Scénario classique (et idéal)

Etape 1 : création du repository local

On démarre la gestion de version :

$ git init

Génération d’un répertoire .git dans le répertoire courant.

$ git init
Initialized empty Git repository in /tmp/.git/

$ ll
total 0
drwxr-xr-x   3 bruel  admin   102 21 jul 17:29 ./
drwxr-xr-x  35 bruel  admin  1190 21 jul 17:29 ../
drwxr-xr-x  10 bruel  admin   340 21 jul 17:29 .git/

Etape 2 : ajout des fichiers

On ajoute les fichiers courants au dépôt :

$ git add .
  • Ne pas forcément tout ajouter (git add *.c par exemple pour ne versionner que les sources).

  • Pensez à créer un fichier .gitignore pour éviter d’ajouter les fichiers indésirables (comme les fichiers de log).

Etape 2 (suite) : vérification

On peut visualiser les actions en vérifiant l'état courant du dépôt :

$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   Generalites.txt
#	deleted:    S3/128056_56.d
...

Etape 3 : Commit

Pour entériner les changements :

$ git commit -m "First draft"
[master (root-commit) 4f40f5d] First draft
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 titi.txt
 create mode 100644 toto.txt
  • Retenez que le commit est uniquement local!

  • Mais même en local, il est bien utile en cas de problème.

Etape 3 (suite) : Gestion "locale"

Exemple de scénario type (suppression exceptionnelle et rattrapage) :

$ rm titi.txt
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	deleted:    titi.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

$ git checkout -f
$ ls titi.txt
titi.txt

Etape 4 : Trouver un hébergement distant

Il existe de nombreux endroits disponibles pour héberger du code libre. Les plus connus sont GitHub et GitLab.

github

Etape 4 (suite) : déclarer le dépôt distant

Après avoir créé un dépôt distant, il n’y a plus qu’à associer ce dépôt distant avec le notre.

$ git remote add origin git@github.com:jmbruel/first_app.git (1)
$ git push -u origin master (2)
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 225 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:jmbruel/first_app.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.
1Il est possible d’avoir plusieurs dépôts distants, celui-ci sera référencé par origin.
2L’option -u origin master permet d’associer une fois pour toute les git push suivants au fait de "pousser" sur la branche master du dépôt origin (comme l’indique la dernière ligne).

Etape 5 : branch, edit, commit, merge

En cas d’édition et de commit local :

$ git checkout
Your branch is ahead of 'origin/master' by 1 commit.

Etape 5 (suite) : branching

est très bon pour créer des branches :

$ git checkout -b testModifTiti
Switched to a new branch 'testModifTiti'
$ git branch
  master
* testModifTiti (1)
1La branche courante est repérée par un *.

Etape 5 (suite) : edit

Après modification :

$ git status
# On branch testModifTiti
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   titi.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

Etape 5 (suite) : commit

On "sauvegarde" les changements :

$ git commit -am "modif de titi"
[testModifTiti 4515b5d] modif de titi
 1 files changed, 7 insertions(+), 0 deletions(-)
  • On ne "sauvegarde" qu’en local!

Etape 5 (suite) : utilisation des branches

On peut "zapper" d’une branche à l’autre à volonté :

$ ll titi*
-rw-rw-r--    1 bruel  staff   331 12 nov 12:39 titi.txt

$ git co master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.

$ ll titi*
-rw-rw-r--    1 bruel  staff     0 12 nov 12:40 titi.txt

Etape 5 (suite) : merge

Maintenant que la branche a été développée (testée, etc.) on peut l’intégrer à la branche principale :

$ git co master
Switched to branch 'master'

$ git merge testModifTiti
Merge made by recursive.
 titi.txt |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

 

  • On peut ensuite détruire la branche devenue inutile git branch -d testModifTiti.

  • C’est une bonne habitude à prendre.

  • Notez que l’historique des modifications (ainsi que les messages de commits successifs ne sont pas perdus).

Etape 6 : push

Maintenant que notre dépôt est satisfaisant, on peut le synchroniser avec le dépôt distant :

$ git push
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 977 bytes, done.
Total 9 (delta 2), reused 0 (delta 0)
To git@github.com:jmbruel/first_app.git
   6103463..3aae48a  master -> master

Etape 7 : pull request (demande)

pull request1

 

pull request2

Etape 7 (suite) : pull request (acceptation)

$ git checkout -b develop origin/develop
$ ...                       (1)
$ git checkout master
$ git merge --no-ff develop (2)
$ git push origin master    (3)
1Vérifiez ce qui va être intégré
2On merge localement pour gérer les problèmes
3On pousse sur master

Dépôts existants

Si vous devez partir d’un dépôt existant :

$ git clone git@github.com:jmbruel/first_app.git
  • Pour obtenir le nom du dépôt distant : git remote -v.

  • Vous avez aussi le nom du dépôt distant dans le fichier .git/config.

Principes de bases

Les 3 espaces

git spaces

Stash

git stash1
git stash2
Même après un git add !

Illustration des branches

Voici une illustration de l’utilisation des branches (tirée de git-scm).

On part d’une situation type :

gf1

Illustration des branches (suite)

On crée une branche (appelée iss53 ici pour indiquer qu’elle traite de l'issue numéro 53) :

$ git checkout -b iss53
gf2
  • n’a créé qu’un pointeur ⇒ aucun espace mémoire perdu.

Illustration des branches (suite)

On modifie et on commit :

$ edit ...
$ git commit -m "blabla iss53"
gf3
On commence à diverger de master

Illustration des branches (suite)

On revient à la branche maître pour tester une autre solution :

$ git checkout master
$ git checkout -b hotfix
$ edit ...
$ git commit -m "blabla hotfix"
gf4

Illustration des branches (suite)

On intègre cette solution à la branche principale :

$ git checkout master
$ git merge hotfix
gf5
Il manque le pointeur HEAD sur mes illustrations
  • utilise ici le fast-forward

Illustration des branches (suite)

On continue à travailler sur la branche iss53 :

$ git branch -d hotfix (1)
$ git checkout iss53
$ edit ...
$ git commit -m "blabla iss53"
1Destruction de la branche devenue redondante avec master.
gf6
On retravaille sur iss53

Illustration des branches (suite)

On intègre cette branche :

$ git checkout master
$ git merge iss53
gf7
Merge sans fast-forward

Illustration des branches (suite)

gf8
Situation finale
  • On part du principe qu’il n’y a pas eu de [conflits]

  • On peut maintenant supprimer iss53

Bonne utilisation

Avoir une procédure concertée

Revenons sur l’exemple type :

git branching

Ne pas versionner n’importe quoi!

Ce qu’il ne faut pas versionner :

  • les exécutables

  • les zip dont le contenu change sans arrêt

  • les images générées

  • tous les binaires en général!

Les "releases"

En on peut taguer des branches et c’est ce mécanisme qui permet de gérer simplement les releases. Dans l’exemple ci-dessous on tague le commit ebb0a7 avec le tag v1.0.

$ git tag -a v1.0 ebb0a7 -m "Release 1.0 as required by client"
$ git tag
v1.0
$ git push origin v1.0
ne pas oublier de "pousser" le tag.

 

On peut voir les détails d’un commit tagué :

$ git show v1.0
tag v1.0
Tagger: Jean-Michel Bruel <jbruel@gmail.com>
Date:   Fri Sep 16 14:27:20 2016 +0200

Release 1.0 as required by client

commit 47da474098d95f8ef5c3ca838be8b87d7a7ed729
Author: Jean-Michel Bruel <jbruel@gmail.com>
Date:   Fri Sep 16 12:38:20 2016 +0200

 

On peut aussi taguer a posteriori :

$ git tag -a v1.2 9fceb02 (1)
1ajoute le tag v1.2 au commit dont le [SHA-1] commence par 9fceb02

Par défaut les tags ne sont pas poussés sur le dépôt distant.

$ git push origin v1.5

La gestion de version n’est pas un long fleuve tranquille

Oups! j’ai oublié un truc

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

Oups! j’ai mis trop de truc

$ git add *.*
$ git reset *.class
Aucun danger

CTRL+Z

$ working on some file README.adoc ...
$ git checkout -- README.adoc
Danger!

Où j’en suis

$ git status

 

gitbash1

 

gitbash2

 

gitbash3

 

gitbash4

Gestion des branches

 

La principale difficulté de vient de la liberté en termes de branches.

Pour faire simple, je vous conseille une gestion qui marche bien pour les petites équipes, tiré de l’excellent livre Pro Git :

 

  • Deux branches seulement: master et develop.

    $ git branch
    * develop     (1)
      master      (2)
    1develop est la branche de travail qui contient la dernière version des codages en cours.
    2master est toujours stable et sert au déploiement

 

  • On fork develop pour traiter un bug ou une feature.

    • On merge dans develop

    • On détruit la branche devenue inutile

 

Ce qui donne le flot suivant dès que vous devez faire une amélioration (corriger un bug ou ajouter une fonctionnalités) :

  • Créer une branche (e.g., fix-451)

  • Travailler sur cette branche

  • Merger cette branche dans develop

  • Rejouer les tests

  • Régler les conflits éventuels

  • Quand tout fonctionne ⇒ [pull-request]

  • On peut livrer à partir de master

Les différents merge

 

Explicit merge

what is a merge

Implicit merge

Via rebase

what is a rebase

Implicit merge

Via fast-forward

what is a fast forward

Squash on merge

squash on merge

merge vs. rebase

Here is an illustration using http://git-school.github.io/visualizing-git/ :

Initial situation:

mergeVsRebase base

 

git merge JMB:

mergeVsRebase merge

 

git rebase JMB:

mergeVsRebase rebase

Gestion des conflits

 

La principale activité du programmeur qui utilise en équipe vient de la gestion des conflits.

À la main

$ git checkout master
$ git merge other_branch
Auto-merging toto.txt
CONFLICT (content): Merge conflict in toto.txt
Automatic merge failed; fix conflicts and then commit the result.
$ more toto.txt
<<<<<<< HEAD (1)
Salut monde
======= (2)
hello world!

>>>>>>> other_branch (3)
$ vi toto.txt (4)
$ git commit (5)

Avec un peu d’aide

Git avancé

Les outils clonés de git dans un dépôt git

Ne pas simplement cloner, car soucis de synchro plus tard.

Faire :

$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Vous devez voir apparaître, en plus du répertoire cloné, un fichier .gitmodules (si c’est la 1ère fois).

Git-Flow

Wrap-up

Résumé des commandes

Voici un schéma pour résumer la philosophie (tiré de http://osteele.com) :

git resume

The End